package com.capybara.account.base;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.annotation.EntityMapping;
import com.github.yulichang.annotation.FieldMapping;
import com.github.yulichang.extension.mapping.mapper.MPJTableInfo;
import com.github.yulichang.extension.mapping.mapper.MPJTableInfoHelper;
import com.github.yulichang.extension.mapping.wrapper.MappingQuery;
import com.github.yulichang.toolkit.SpringContentUtils;
import com.github.yulichang.wrapper.MPJLambdaWrapper;

import java.lang.reflect.Field;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 我 Lambda 包装器
 *
 * @author guochaowang
 * @date 2024/09/12
 */
public class MyLambdaWrapper<T> extends MPJLambdaWrapper<T> {
    private static Pattern humpPattern = Pattern.compile("[A-Z]");
    private final int page;
    private final int size;

    public <R> MyLambdaWrapper(Class<T> clazz, int page, int size) {
        super(clazz);
        this.page = page;
        this.size = size;
    }

    public <R, P extends IPage<R>> Page<R> page(Class<R> resultType) {
        return super.page(new Page<>(page, size), resultType);
    }

    public <P extends IPage<T>> Page<T> page() {
        return super.page(new Page<>(page, size));
    }

    public <R, P extends IPage<R>> List<R> pageDeep(P page, Class<R> resultType) {
        Class<T> entityClass = getEntityClass();
        Field[] fields = resultType.getDeclaredFields();
        IPage<R> paged = super.page(page, resultType);
        List<R> records = paged.getRecords();
        extracted(resultType, fields, entityClass, records);
        return records;
    }

    public <R> List<R> listDeep(Class<R> resultType) {
        Class<T> entityClass = getEntityClass();
        Field[] fields = resultType.getDeclaredFields();
        List<R> list = super.list(resultType);
        extracted(resultType, fields, entityClass, list);
        return list;
    }

    private <R> void extracted(Class<R> resultType, Field[] fields, Class<T> entityClass, List<R> records) {
        for (Field item : fields) {
            if (item.isAnnotationPresent(FieldMapping.class)) {
                isFieldMapping(resultType, item, entityClass, records);
            } else if (item.isAnnotationPresent(EntityMapping.class)) {
                try {
                    isEntityMapping(resultType, item, entityClass, records);
                } catch (NoSuchFieldException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    private <R> void isEntityMapping(Class<R> resultType, Field item, Class<T> entityClass, List<R> list) throws NoSuchFieldException {
        EntityMapping fieldMapping = item.getAnnotation(EntityMapping.class);
        String thisFieldStr = fieldMapping.thisField();
        String joinField = fieldMapping.joinField();
        Class<?> tag = fieldMapping.tag();
        Field thisField = getFieldInHierarchy(tag, thisFieldStr);
        Field thisJoinField = getFieldInHierarchy(entityClass, thisFieldStr);
        if (thisJoinField == null) {
            return;
        }
        thisJoinField.setAccessible(true);
        // 拿出名字为thisField的属性
        List<Object> itemList = list.stream().map(v -> {
            try {
                return thisJoinField.get(v);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }).toList();
        if (CollectionUtils.isNotEmpty(itemList)) {
            List<?> list2 = getFieldValue(tag, itemList, joinField);
            Field targetField = resultType.getDeclaredField(item.getName());
            targetField.setAccessible(true);
            if (Collection.class.isAssignableFrom(targetField.getType())) {
                Map<Object, ? extends List<?>> listMap = list2.stream().collect(Collectors.groupingBy(v -> {
                    try {
                        if (thisField != null) {
                            return thisField.get(v);
                        }
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                    return new Object();
                }));
                for (R r : list) {
                    try {
                        String s1 = String.valueOf(thisJoinField.get(r));
                        targetField.set(r, listMap.get(s1));
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                }
            } else {
                for (R r : list) {
                    try {
                        for (Object v2 : list2) {
                            String s2 = String.valueOf(thisField.get(v2));
                            try {
                                String s1 = String.valueOf(thisJoinField.get(r));
                                if (s2.equals(s1)) {
                                    targetField.set(r, v2);
                                }
                            } catch (IllegalAccessException e) {
                                throw new RuntimeException(e);
                            }
                        }
                    } catch (Exception e) {
                        throw new RuntimeException("Failed to set field value", e);
                    }
                }
            }
        }
    }

    private <F> List<F> getFieldValue(Class<F> tag, List<Object> itemList, String joinField) {
        BaseMapper<F> baseMapper = getJoinMapper(tag);
        MappingQuery<F> wrapper = new MappingQuery<>();
        wrapper.in(joinField, itemList);
        return baseMapper.selectList(wrapper);
    }

    private <R> void isFieldMapping(Class<R> resultType, Field item, Class<T> entityClass, List<R> list) {
        FieldMapping fieldMapping = item.getAnnotation(FieldMapping.class);
        String thisField = fieldMapping.thisField();
        String joinField = fieldMapping.joinField();
        String select = fieldMapping.select();
        Class<?> tag = fieldMapping.tag();
        Field thisJoinField = getFieldInHierarchy(entityClass, thisField);
        if (thisJoinField == null) {
            return;
        }
        thisJoinField.setAccessible(true);
        // 拿出名字为thisField的属性
        List<Object> itemList = list.stream().map(v -> {
            try {
                return thisJoinField.get(v);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }).toList();
        if (CollectionUtils.isNotEmpty(itemList)) {
            List<Map<String, Object>> maps = getFieldValue(tag, itemList, joinField, select);
            for (R r : list) {
                try {
                    Field targetField = resultType.getDeclaredField(item.getName());
                    targetField.setAccessible(true);
                    for (Map<String, Object> v2 : maps) {
                        String s1 = String.valueOf(v2.get(fieldMapping.thisField()));
                        try {
                            String s2 = String.valueOf(thisJoinField.get(r));
                            if (s1.equals(s2)) {
                                targetField.set(r, v2.get(fieldMapping.joinField()));
                            }
                        } catch (IllegalAccessException e) {
                            throw new RuntimeException(e);
                        }
                    }
                } catch (NoSuchFieldException e) {
                    throw new RuntimeException("Failed to set field value", e);
                }
            }
        }
    }

    public <F> BaseMapper<F> getJoinMapper(Class<F> clazz) {
        MPJTableInfo joinTableInfo = MPJTableInfoHelper.getTableInfo(clazz);
        if (joinTableInfo == null) {
            throw ExceptionUtils.mpe("未注册 mapper " + clazz.getName());
        }
        return SpringContentUtils.getMapper(joinTableInfo.getEntityClass());
    }

    private <
            F> List<Map<String, Object>> getFieldValue(Class<F> tag, List<Object> itemList, String joinField, String select) {
        BaseMapper<F> baseMapper = getJoinMapper(tag);
        MappingQuery<F> wrapper = new MappingQuery<>();
        wrapper.in(joinField, itemList).select(humpToLine2(select), joinField);
        return baseMapper.selectMaps(wrapper);
    }

    public static Field getFieldInHierarchy(Class<?> clazz, String fieldName) {
        try {
            return clazz.getDeclaredField(fieldName);
        } catch (NoSuchFieldException e) {
            if (clazz.getSuperclass() != null) {
                return getFieldInHierarchy(clazz.getSuperclass(), fieldName);
            }
        }
        return null;
    }

    private static String humpToLine2(String str) {
        Matcher matcher = humpPattern.matcher(str);
        StringBuilder sb = new StringBuilder();
        while (matcher.find()) {
            matcher.appendReplacement(sb, "_" + matcher.group(0).toLowerCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }
}
